home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / system / mail / transpor / ifmail23.z / ifmail23 / ifmail / ifcico / xmrecv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-09  |  10.5 KB  |  506 lines

  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <fcntl.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <time.h>
  9. #include <utime.h>
  10. #include "session.h"
  11. #include "ttyio.h"
  12. #include "statetbl.h"
  13. #include "xutil.h"
  14. #include "lutil.h"
  15. #include "config.h"
  16.  
  17. #define XMBLKSIZ 128
  18.  
  19. extern unsigned long sequencer(void);
  20. extern unsigned short crc16(char*,int);
  21. extern unsigned char checksum(char*,int);
  22. extern time_t sl2mtime(time_t);
  23. extern time_t tl2mtime(time_t);
  24. extern int m7recv(char*);
  25.  
  26. static int xm_recv(void);
  27. static int resync(off_t);
  28. FILE *openfile(char*,time_t,off_t,off_t*,int(*)(off_t));
  29. int closefile(int);
  30. static int closeit(int);
  31. static char *recvname=NULL;
  32. static char *fpath=NULL;
  33. static FILE *fp=NULL;
  34. static int last;
  35. static time_t stm,etm;
  36. static off_t startofs;
  37. static long recv_blk;
  38.  
  39. int xmrecv(char*);
  40. int xmrecv(name)
  41. char *name;
  42. {
  43.     int rc;
  44.  
  45.     debug(11,"start xmodem receive \"%s\"",name);
  46.     recvname=name;
  47.     last=0;
  48.     rc=xm_recv();
  49.     if (fp) closeit(0);
  50.     TTYWAIT(1);
  51.     if (rc) return -1;
  52.     else if (last) return 1;
  53.     else return 0;
  54. }
  55.  
  56. int closeit(success)
  57. int success;
  58. {
  59.     off_t endofs;
  60.  
  61.     endofs=recv_blk*XMBLKSIZ;
  62.     (void)time(&etm);
  63.     if (etm == stm) etm++;
  64.     loginf("%s %lu bytes in %lu seconds (%lu cps)",
  65.         success?"received":"dropped after",
  66.         endofs-startofs,etm-stm,
  67.         (endofs-startofs)/(etm-stm));
  68.     fp=NULL;
  69.     return closefile(success);
  70. }
  71.  
  72. SM_DECL(xm_recv,"xmrecv")
  73. SM_STATES
  74.     sendnak0,waitblk0,
  75.     sendnak,waitblk,recvblk,sendack,checktelink,
  76.     recvm7,goteof
  77. SM_NAMES
  78.     "sendnak0","waitblk0",
  79.     "sendnak","waitblk","recvblk","sendack","checktelink",
  80.     "recvm7","goteof"
  81. SM_EDECL
  82.  
  83.     int tmp;
  84.     int crcmode=session_flags&FTSC_XMODEM_CRC;
  85.     int count=0,junk=0,cancount=0;
  86.     int header;
  87.     struct _xmblk {
  88.         unsigned char n1,n2;
  89.         char data[XMBLKSIZ];
  90.         unsigned char c1,c2;
  91.     } xmblk;
  92.     unsigned short localcrc,remotecrc;
  93.     unsigned char localcs,remotecs;
  94.     long ackd_blk=-1L;
  95.     long next_blk=1L;
  96.     long last_blk=0L;
  97.     off_t resofs;
  98.     int telink=0;
  99.     char tmpfname[16];
  100.     off_t wsize;
  101.     time_t remtime=0L;
  102.     off_t remsize=0;
  103.     char ctt[32];
  104.  
  105.     (void)time(&stm);
  106.     recv_blk=-1L;
  107.  
  108.     if (recvname) strncpy(tmpfname,recvname,sizeof(tmpfname)-1);
  109.     else tmpfname[0]='\0';
  110.  
  111. SM_START(sendnak0)
  112.  
  113. SM_STATE(sendnak0)
  114.  
  115.     if (count++ > 9)
  116.     {
  117.         loginf("too many errors while xmodem receive init");
  118.         SM_ERROR;
  119.     }
  120.     if ((ackd_blk < 0) && crcmode && (count > 4))
  121.     {
  122.         debug(11,"no responce to 'C', try checksum mode");
  123.         session_flags &= ~FTSC_XMODEM_CRC;
  124.         crcmode=0;
  125.     }
  126.  
  127.     if (crcmode) PUTCHAR('C');
  128.     else PUTCHAR(NAK);
  129.  
  130.     junk=0;
  131.  
  132.     SM_PROCEED(waitblk0);
  133.  
  134. SM_STATE(waitblk0)
  135.  
  136.     header=GETCHAR(5);
  137.     if (header == TIMEOUT)
  138.     {
  139.         debug(11,"timeout waiting for xmodem block 0 header, count=%d",
  140.             count);
  141.         if ((count > 2) && (session_flags & SESSION_IFNA))
  142.         {
  143.             loginf("timeout waiting for file in WaZOO session, report success");
  144.             last=1;
  145.             SM_SUCCESS;
  146.         }
  147.         SM_PROCEED(sendnak0);
  148.     }
  149.     else if (header < 0) {SM_ERROR;}
  150.     else switch (header)
  151.     {
  152.     case EOT:    last=1;
  153.             SM_SUCCESS;
  154.             break;
  155.     case CAN:    loginf("got CAN while xmodem receive init");
  156.             SM_ERROR;
  157.             break;
  158.     case SOH:    SM_PROCEED(recvblk);
  159.             break;
  160.     case SYN:    telink=1;
  161.             SM_PROCEED(recvblk);
  162.             break;
  163.     case ACK:    telink=1;
  164.             SM_PROCEED(recvm7);
  165.             break;
  166.     case TSYNC:    SM_PROCEED(sendnak0);
  167.             break;
  168.     case NAK:
  169.     case 'C':    PUTCHAR(EOT); /* other end still waiting us to send? */
  170.             SM_PROCEED(waitblk0);
  171.             break;
  172.     default:    debug(11,"got '%s' waiting for block 0",
  173.                 printablec(header));
  174.             if (junk++ > 300) {SM_PROCEED(sendnak0);}
  175.             else {SM_PROCEED(waitblk0);}
  176.             break;
  177.     }
  178.  
  179. SM_STATE(sendnak)
  180.  
  181.     if (ackd_blk < 0) {SM_PROCEED(sendnak0);}
  182.  
  183.     if (count++ > 9)
  184.     {
  185.         loginf("too many errors while xmodem receive");
  186.         SM_ERROR;
  187.     }
  188.  
  189.     junk=0;
  190.  
  191.     if (remote_flags&FTSC_XMODEM_RES)
  192.     {
  193.         if (resync(ackd_blk*XMBLKSIZ)) {SM_ERROR;}
  194.         else {SM_PROCEED(waitblk);}
  195.     }
  196.     else /* simple NAK */
  197.     {
  198.         debug(11,"negative acknowlege block %ld",ackd_blk+1);
  199.  
  200.         PUTCHAR(NAK);
  201.         PUTCHAR(ackd_blk+1);
  202.         PUTCHAR(~(ackd_blk+1));
  203.         if (STATUS) {SM_ERROR;}
  204.         else {SM_PROCEED(waitblk);}
  205.     }
  206.  
  207. SM_STATE(sendack)
  208.  
  209.     ackd_blk=recv_blk;
  210.     count=0;
  211.     cancount=0;
  212.     debug(11,"acknowlege block %ld",ackd_blk);
  213.  
  214.     PUTCHAR(ACK);
  215.     PUTCHAR(ackd_blk);
  216.     PUTCHAR(~ackd_blk);
  217.     if (STATUS) {SM_ERROR;}
  218.     SM_PROCEED(waitblk);
  219.  
  220. SM_STATE(waitblk)
  221.  
  222.     header=GETCHAR(15);
  223.     if (header == TIMEOUT)
  224.     {
  225.         debug(11,"timeout waiting for xmodem block header, count=%d",
  226.             count);
  227.         SM_PROCEED(sendnak);
  228.     }
  229.     else if (header < 0) {SM_ERROR;}
  230.     else switch (header)
  231.     {
  232.     case EOT:    if (last_blk && (ackd_blk != last_blk))
  233.             {
  234.                 debug(11,"false EOT after %ld block, need after %ld",
  235.                     ackd_blk,last_blk);
  236.                 SM_PROCEED(waitblk);
  237.             }
  238.             else {SM_PROCEED(goteof);}
  239.             break;
  240.     case CAN:    if (cancount++ > 4)
  241.             {
  242.                 closeit(0);
  243.                 loginf("got CAN while xmodem receive");
  244.                 SM_ERROR;
  245.             }
  246.             else {SM_PROCEED(waitblk);}
  247.             break;
  248.     case SOH:    SM_PROCEED(recvblk);
  249.             break;
  250.     default:    if (header < ' ') debug(11,"got '\\%03o' waiting SOH",
  251.                         header);
  252.             else debug(11,"got '%c' waiting SOH",header);
  253.             if (junk++ > 200) {SM_PROCEED(sendnak);}
  254.             else {SM_PROCEED(waitblk);}
  255.             break;
  256.     }
  257.  
  258. SM_STATE(recvblk)
  259.  
  260.     GET((char*)&xmblk,(crcmode && (header != SYN))?
  261.                 sizeof(xmblk):
  262.                 sizeof(xmblk)-1,15);
  263.     if (STATUS == STAT_TIMEOUT)
  264.     {
  265.         debug(11,"xmrecv timeout waiting for block body");
  266.         SM_PROCEED(sendnak);
  267.     }
  268.     if (STATUS) {SM_ERROR;}
  269.     if ((xmblk.n1&0xff) != ((~xmblk.n2)&0xff))
  270.     {
  271.         debug(11,"bad block number: 0x%02x/0x%02x (0x%02x)",
  272.             xmblk.n1,xmblk.n2,(~xmblk.n2)&0xff);
  273.         SM_PROCEED(waitblk);
  274.     }
  275.     recv_blk=ackd_blk-(ackd_blk-(unsigned)xmblk.n1);
  276.     if (crcmode && (header != SYN))
  277.     {
  278.         remotecrc=(short)xmblk.c1<<8|xmblk.c2;
  279.         localcrc=crc16(xmblk.data,sizeof(xmblk.data));
  280.         if (remotecrc != localcrc)
  281.         {
  282.             debug(11,"bad crc: 0x%04x/0x%04x",remotecrc,localcrc);
  283.             if (recv_blk == (ackd_blk+1)) {SM_PROCEED(sendnak);}
  284.             else {SM_PROCEED(waitblk);}
  285.         }
  286.     }
  287.     else
  288.     {
  289.         remotecs=xmblk.c1;
  290.         localcs=checksum(xmblk.data,sizeof(xmblk.data));
  291.         if (remotecs != localcs)
  292.         {
  293.             debug(11,"bad checksum: 0x%02x/0x%02x",remotecs,localcs);
  294.             if (recv_blk == (ackd_blk+1)) {SM_PROCEED(sendnak);}
  295.             else {SM_PROCEED(waitblk);}
  296.         }
  297.     }
  298.     if ((ackd_blk == -1L) && (recv_blk == 0L)) {SM_PROCEED(checktelink);}
  299.     if ((ackd_blk == -1L) && (recv_blk == 1L))
  300.     {
  301.         if (count < 3) {SM_PROCEED(sendnak0);}
  302.         else ackd_blk=0L;
  303.     }
  304.     if (recv_blk < (ackd_blk+1L))
  305.     {
  306.         debug(11,"old block number %ld after %ld, go on",
  307.             recv_blk,ackd_blk);
  308.         SM_PROCEED(waitblk);
  309.     }
  310.     else if (recv_blk > (ackd_blk+1L))
  311.     {
  312.         debug(11,"bad block order: %ld after %ld, go on",
  313.             recv_blk,ackd_blk);
  314.         SM_PROCEED(waitblk);
  315.     }
  316.  
  317.     debug(11,"received block %ld \"%s\"",
  318.         recv_blk,printable(xmblk.data,128));
  319.  
  320.     if (fp == NULL)
  321.     {
  322.         if ((fp=openfile(tmpfname,remtime,remsize,&resofs,resync)) == NULL)
  323.         {
  324.             SM_ERROR;
  325.         }
  326.         else
  327.         {
  328.             if (resofs) ackd_blk=(resofs-1)/XMBLKSIZ+1L;
  329.             else ackd_blk=-1L;
  330.         }
  331.         startofs=resofs;
  332.         loginf("xmodem receive: \"%s\"",tmpfname);
  333.     }
  334.     
  335.     if (recv_blk > next_blk)
  336.     {
  337.         logerr("xmrecv internal error: recv_blk %ld > next_blk %ld",
  338.             recv_blk,next_blk);
  339.         SM_ERROR;
  340.     }
  341.     if (recv_blk == next_blk)
  342.     {
  343.         if (recv_blk == last_blk) wsize=remsize%XMBLKSIZ;
  344.         else wsize=XMBLKSIZ;
  345.         if (wsize == 0) wsize=XMBLKSIZ;
  346.         if ((tmp=fwrite(xmblk.data,wsize,1,fp)) != 1)
  347.         {
  348.             logerr("$error writing block %l (%d bytes) to file \"%s\" (fwrite return %d)",
  349.                 recv_blk,wsize,fpath,tmp);
  350.             SM_ERROR;
  351.         }
  352.         else debug(11,"Block %ld size %d written (ret %d)",
  353.             recv_blk,wsize,tmp);
  354.         next_blk++;
  355.     }
  356.     else
  357.     {
  358.         debug(11,"recv_blk %ld < next_blk %ld, ack without writing",
  359.             recv_blk,next_blk);
  360.     }
  361.     SM_PROCEED(sendack);
  362.  
  363. SM_STATE(checktelink)
  364.  
  365.     debug(11,"checktelink got \"%s\"",printable(xmblk.data,45));
  366.     if (tmpfname[0] == '\0')
  367.     {
  368.         strncpy(tmpfname,xmblk.data+8,16);
  369.     }
  370.     else
  371.     {
  372.         loginf("Remote uses %s",printable(xmblk.data+25,-14));
  373.         debug(11,"Remote file name \"%s\" discarded",
  374.             printable(xmblk.data+8,-16));
  375.     }
  376.     remsize=((off_t)xmblk.data[0])+((off_t)xmblk.data[1]<<8)+
  377.         ((off_t)xmblk.data[2]<<16)+((off_t)xmblk.data[3]<<24);
  378.     last_blk=(remsize-1)/XMBLKSIZ+1;
  379.     if (header == SOH)
  380.     {
  381.         remtime=sl2mtime(((time_t)xmblk.data[4])+
  382.             ((time_t)xmblk.data[5]<<8)+
  383.             ((time_t)xmblk.data[6]<<16)+
  384.             ((time_t)xmblk.data[7]<<24));
  385.         if (xmblk.data[40]) remote_flags |= FTSC_XMODEM_SLO;
  386.         else remote_flags &= ~FTSC_XMODEM_SLO;
  387.         if (xmblk.data[41]) remote_flags |= FTSC_XMODEM_RES;
  388.         else remote_flags &= ~FTSC_XMODEM_RES;
  389.         if (xmblk.data[42]) remote_flags |= FTSC_XMODEM_XOF;
  390.         else remote_flags &= ~FTSC_XMODEM_XOF;
  391.     }
  392.     else /* Telink */
  393.     {
  394.         remtime=tl2mtime(((time_t)xmblk.data[4])+
  395.             ((time_t)xmblk.data[5]<<8)+
  396.             ((time_t)xmblk.data[6]<<16)+
  397.             ((time_t)xmblk.data[7]<<24));
  398. /*
  399.         if (xmblk.data[40]) remote_flags |= FTSC_XMODEM_NOACKS;
  400.         else remote_flags &= ~FTSC_XMODEM_NOACKS;
  401. */
  402.         if (xmblk.data[41]) session_flags |= FTSC_XMODEM_CRC;
  403.         else session_flags &= ~FTSC_XMODEM_CRC;
  404.     }
  405.     debug(11,"%s block, session_flags=0x%04x, remote_flags=0x%04x",
  406.         (header == SYN)?"Telink":"Sealink",session_flags,remote_flags);
  407.     strcpy(ctt,date(remtime));
  408.     debug(11,"Remote file size %lu timestamp %s",remsize,ctt);
  409.  
  410.     if ((fp=openfile(tmpfname,remtime,remsize,&resofs,resync)) == NULL)
  411.     {
  412.         SM_ERROR;
  413.     }
  414.     if (resofs) ackd_blk=(resofs-1)/XMBLKSIZ+1L;
  415.     else ackd_blk=-1L;
  416.     startofs=resofs;
  417.  
  418.     loginf("xmodem receive: \"%s\" %ld bytes dated %s",
  419.         tmpfname,remsize,ctt);
  420.  
  421.     if (ackd_blk == -1) {SM_PROCEED(sendack);}
  422.     else {SM_PROCEED(waitblk);}
  423.  
  424. SM_STATE(recvm7)
  425.  
  426.     switch (m7recv(tmpfname))
  427.     {
  428.     case 0:    ackd_blk=0; SM_PROCEED(sendnak); break;
  429.     case 1:    last=1; SM_SUCCESS; break;
  430.     default: SM_PROCEED(sendnak);
  431.     }
  432.  
  433. SM_STATE(goteof)
  434.  
  435.     closeit(1);
  436.     if (ackd_blk == -1L) last=1;
  437.     else
  438.     {
  439.         ackd_blk++;
  440.         PUTCHAR(ACK);
  441.         PUTCHAR(ackd_blk);
  442.         PUTCHAR(~ackd_blk);
  443.     }
  444.     if (STATUS) {SM_ERROR;}
  445.     SM_SUCCESS;
  446.  
  447. SM_END
  448. SM_RETURN
  449.  
  450.  
  451. int resync(resofs)
  452. off_t resofs;
  453. {
  454.     char resynbuf[16];
  455.     short lcrc;
  456.     int count=0;
  457.     int gotack,gotnak;
  458.     int c;
  459.     long sblk;
  460.  
  461.     debug(11,"trying to resync at offset %ld",resofs);
  462.  
  463.     sblk=resofs/XMBLKSIZ+1;
  464.     sprintf(resynbuf,"%ld",sblk);
  465.     lcrc=crc16(resynbuf,strlen(resynbuf));
  466.     gotack=0;
  467.     gotnak=0;
  468.  
  469.     do
  470.     {
  471.         count++;
  472.         PUTCHAR(SYN);
  473.         PUTSTR(resynbuf);
  474.         PUTCHAR(ETX);
  475.         PUTCHAR(lcrc&0xff);
  476.         PUTCHAR(lcrc>>8);
  477.         do
  478.         {
  479.             if ((c=GETCHAR(5)) == ACK)
  480.             {
  481.                 if ((c=GETCHAR(1)) == SOH) gotack=1;
  482.                 UNGETCHAR(c);
  483.             }
  484.             else if (c == NAK)
  485.             {
  486.                 if ((c=GETCHAR(1)) == TIMEOUT) gotnak=1;
  487.                 UNGETCHAR(c);
  488.             }
  489.         }
  490.         while (!gotack && !gotnak && (c >= 0));
  491.         if ((c < 0) && (c != TIMEOUT)) return 1;
  492.     }
  493.     while (!gotack && !gotnak && (count < 6));
  494.  
  495.     if (gotack)
  496.     {
  497.         debug(11,"resyncing at offset %ld",resofs);
  498.         return 0;
  499.     }
  500.     else
  501.     {
  502.         loginf("sealink resync at offset %ld failed",resofs);
  503.         return 1;
  504.     }
  505. }
  506.